home *** CD-ROM | disk | FTP | other *** search
/ Clickx 47 / Clickx 47.iso / assets / software / Miro_Installer.exe / xulrunner / python / selection.py < prev    next >
Encoding:
Python Source  |  2008-01-10  |  24.6 KB  |  657 lines

  1. # Miro - an RSS based video player application
  2. # Copyright (C) 2005-2007 Participatory Culture Foundation
  3. #
  4. # This program is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation; either version 2 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  17.  
  18. """Handle selection."""
  19.  
  20. from copy import copy
  21.  
  22. import app
  23. import database
  24. import eventloop
  25. import folder
  26. import config
  27. import prefs
  28. import guide
  29. import item
  30. import tabs
  31. import playlist
  32. import feed
  33. import views
  34. import indexes
  35. import template
  36. import util
  37. from gtcache import gettext as _
  38.  
  39. def getID(obj):
  40.     """Gets an ID to use for an object.  For tabs, this is the object ID that
  41.     maps to the tab.  For other objects this is the actual DDBObject ID."""
  42.     if isinstance(obj, tabs.Tab):
  43.         return obj.objID()
  44.     else:
  45.         return obj.getID()
  46.  
  47. class SelectionArea(object):
  48.     """Represents an area that holds a selection.  Currently we have 2
  49.     SelectionAreas, the tab list and the item list.  SelectionAreas hold
  50.     several database views, for instance the tab list contains
  51.     views.guideTabs, views.staticTabs, views.feedTabs and views.playlistTabs.
  52.     All the items selected in an area must be in a single view.
  53.  
  54.     Member variables:
  55.  
  56.     currentView -- The view that items are currently selected in, or None if
  57.         there are no items selected.
  58.     currentSelection -- set of object IDs that are currently selected.
  59.     """
  60.  
  61.     def __init__(self, selectionHandler):
  62.         self.currentSelection = set()
  63.         self.currentView = None
  64.         self.handler = selectionHandler
  65.  
  66.     def switchView(self, view):
  67.         if self.currentView == view:
  68.             return
  69.         if self.currentView:
  70.             self.clearSelection()
  71.         self.currentView = view
  72.         self.currentView.addRemoveCallback(self.onRemove)
  73.         self.currentView.addAddCallback(self.onAdd)
  74.         self.currentView.addViewUnlinkCallback(self.onViewUnlinked)
  75.  
  76.     def selectItem(self, view, id):
  77.         self.switchView(view)
  78.         obj = view.getObjectByID(id)
  79.         self.currentSelection.add(id)
  80.         obj.setSelected(True)
  81.  
  82.     def deselectItem(self, view, id):
  83.         if view != self.currentView:
  84.             raise ValueError("view != current view in deselectItem()")
  85.         obj = view.getObjectByID(id)
  86.         self.currentSelection.remove(id)
  87.         obj.setSelected(False)
  88.  
  89.     def isSelected(self, view, id):
  90.         return self.currentView == view and id in self.currentSelection
  91.  
  92.     def toggleItemSelect(self, view, id):
  93.         self.switchView(view)
  94.         if id in self.currentSelection:
  95.             self.deselectItem(view, id)
  96.         else:
  97.             self.selectItem(view, id)
  98.  
  99.     def clearSelection(self):
  100.         """Clears the current selection."""
  101.  
  102.         for obj in self.getObjects():
  103.             obj.setSelected(False)
  104.         self.currentSelection = set()
  105.         if self.currentView is not None:
  106.             self.currentView.removeRemoveCallback(self.onRemove)
  107.             self.currentView.removeAddCallback(self.onAdd)
  108.             self.currentView = None
  109.  
  110.     def selectAll(self):
  111.         """Select all objects in the current View."""
  112.         
  113.         view = self.currentView
  114.         if view:
  115.             for obj in view:
  116.                 if obj.getID not in self.currentSelection:
  117.                     self.selectItem(view, obj.getID())
  118.  
  119.     def calcExtendRange(self, view, id):
  120.         idIsBefore = False
  121.         gotFirst = False
  122.         firstID = lastID = None
  123.         for obj in view:
  124.             objID = getID(obj)
  125.             if objID == id and not gotFirst:
  126.                 idIsBefore = True
  127.             if objID in self.currentSelection:
  128.                 if not gotFirst:
  129.                     firstID = objID
  130.                     gotFirst = True
  131.                 lastID = objID
  132.         if firstID is None or lastID is None:
  133.             raise AssertionError("Couldn't find my selected IDs")
  134.         if idIsBefore:
  135.             return id, lastID
  136.         else:
  137.             return firstID, id
  138.  
  139.     def extendSelection(self, view, id):
  140.         """Extends the selection in response to a shift-select.  If id is on
  141.         top of the current selection, we will select everything between the id
  142.         and the last selected item.  If id is below it or in the middle, we
  143.         will select between the first selected item and id.  
  144.         """
  145.  
  146.         self.switchView(view)
  147.         if len(self.currentSelection) == 0:
  148.             return self.selectItem(view, id)
  149.         firstID, lastID = self.calcExtendRange(view, id)
  150.         self.selectBetween(view, firstID, lastID)
  151.  
  152.     def selectBetween(self, view, firstID, lastID):
  153.         """Select all items in view between firstID and lastID."""
  154.  
  155.         self.switchView(view)
  156.         selecting = False
  157.         toSelect = []
  158.         for obj in view:
  159.             id = getID(obj)
  160.             if selecting and id not in self.currentSelection:
  161.                 toSelect.append(id)
  162.             if id == firstID:
  163.                 selecting = True
  164.                 if id not in self.currentSelection:
  165.                     toSelect.append(id)
  166.             if id == lastID:
  167.                 break
  168.         for id in toSelect:
  169.             self.selectItem(view, id)
  170.  
  171.     def onRemove(self, obj, id):
  172.         if id in self.currentSelection:
  173.             self.currentSelection.remove(id)
  174.             if obj.idExists():
  175.                 obj.setSelected(False)
  176.  
  177.     def setObjectsActive(self, newValue):
  178.         """Iterate through all selected objects and call setActive on them,
  179.         passing in newValue.
  180.         """
  181.  
  182.         for obj in self.getObjects():
  183.             obj.setActive(newValue)
  184.  
  185.     def onAdd(self, obj, id):
  186.         if obj.getSelected() and id not in self.currentSelection:
  187.             # this happens when we remove/add the object to reorder it in a
  188.             # playlist
  189.             self.currentSelection.add(id)
  190.  
  191.     def onViewUnlinked(self):
  192.         self.clearSelection()
  193.  
  194.     def getTypesDetailed(self):
  195.         """Get the type of objects that are selected.  
  196.  
  197.         Returns a set, containing all the type of objects selected.  The
  198.         members will be one of the following:
  199.  
  200.         'item', 'downloadeditem' 'playlisttab', playlistfoldertab,
  201.         'channeltab', 'channelfoldertab', 'guidetab', 'addedguidetab',
  202.         'statictab'.
  203.  
  204.         """
  205.  
  206.         types = set()
  207.         for obj in self.getObjects():
  208.             if isinstance(obj, item.Item):
  209.                 if obj.isDownloaded():
  210.                     newType = 'downloadeditem'
  211.                 else:
  212.                     newType = 'item'
  213.             elif isinstance(obj, tabs.Tab):
  214.                 objClass = obj.obj.__class__
  215.                 if objClass == playlist.SavedPlaylist:
  216.                     newType = 'playlisttab'
  217.                 elif objClass == folder.PlaylistFolder:
  218.                     newType = 'playlistfoldertab'
  219.                 elif objClass == feed.Feed:
  220.                     newType = 'channeltab'
  221.                 elif objClass == folder.ChannelFolder:
  222.                     newType = 'channelfoldertab'
  223.                 elif objClass == guide.ChannelGuide:
  224.                     if obj.obj.getDefault():
  225.                         newType = 'guidetab'
  226.                     else:
  227.                         newType = 'addedguidetab'
  228.                 elif objClass == tabs.StaticTab:
  229.                     newType = 'statictab'
  230.                 else:
  231.                     raise ValueError("Bad selected tab type: %s" % obj.obj)
  232.             else:
  233.                 raise ValueError("Bad selected object type: %s" % obj)
  234.             types.add(newType)
  235.         self.simplifyTypes(types) 
  236.         # we don't care about the result of simplifyTypes, but the error
  237.         # checking is useful
  238.         return types
  239.  
  240.     def simplifyTypes(self, types):
  241.         if len(types) == 0:
  242.             return None
  243.         elif types.issubset(set(["item", "downloadeditem"])):
  244.                 return "item"
  245.         elif types.issubset(set(["playlistfoldertab", "playlisttab"])):
  246.                 return "playlisttab"
  247.         elif types.issubset(set(["channelfoldertab", "channeltab"])):
  248.                 return "channeltab"
  249.         elif len(types) == 1:
  250.             for type in types:
  251.                 return type
  252.         else:
  253.             raise ValueError("Multiple types selected: %s" % types)
  254.  
  255.     def getType(self):
  256.         """Get the simplified version of the type of objects that are
  257.         selected.  getType() works like getTypesDetailed(), but it just
  258.         returns one value.  It doesn't differentiate between playlists and
  259.         playlist folders, and items and downloaded items for example.
  260.  
  261.         The return value will be one of
  262.  
  263.         "item", "playlisttab", "channeltab", 'guidetab', 'addedguidetab',
  264.         'statictab', or None if nothing is selected.  
  265.  
  266.         """
  267.  
  268.         return self.simplifyTypes(self.getTypesDetailed())
  269.  
  270.     def getObjects(self):
  271.         view = self.currentView
  272.         return [view.getObjectByID(id) for id in self.currentSelection]
  273.  
  274.     def firstBeforeSelection(self, iterator):
  275.         """Go through iterator and find the first item that is selected.
  276.         Returns the item immediately before that one.
  277.  
  278.         Returns None if the first item in iterator is selected, or no items
  279.         are selected.
  280.         """
  281.  
  282.         lastItem = None
  283.         for item in iterator:
  284.             if item.getID() in self.currentSelection:
  285.                 return lastItem
  286.             lastItem = item
  287.         return None
  288.  
  289.     def firstAfterSelection(self, iterator):
  290.         """Like firstBeforeSelection, but returns the first item following the
  291.         last selected item in iterator.
  292.         """
  293.  
  294.         retval = None
  295.         lastSelected = False
  296.         for item in iterator:
  297.             if item.getID() in self.currentSelection:
  298.                 lastSelected = True
  299.             elif lastSelected:
  300.                 lastSelected = False
  301.                 retval = item
  302.         return retval
  303.  
  304. class TabSelectionArea(SelectionArea):
  305.     """Selection area for the tablist.  This has a couple special cases to
  306.     ensure that we always have at least one tab selected.
  307.     """
  308.  
  309.     def selectItem(self, view, id):
  310.         SelectionArea.selectItem(self, view, id)
  311.         self.moveCursorToSelection()
  312.  
  313.     def deselectItem(self, view, id):
  314.         SelectionArea.deselectItem(self, view, id)
  315.         self.moveCursorToSelection()
  316.  
  317.     def moveCursorToSelection(self):
  318.         for id in self.currentSelection:
  319.             self.currentView.moveCursorToID(id)
  320.             break
  321.  
  322.     def toggleItemSelect(self, view, id):
  323.         # Don't let a control select deselect the last selected item in the
  324.         # tab list.
  325.         if self.currentSelection == set([id]):
  326.             return
  327.         else:
  328.             return SelectionArea.toggleItemSelect(self, view, id)
  329.  
  330.     def onRemove(self, obj, id):
  331.         SelectionArea.onRemove(self, obj, id)
  332.         # We may be removing/adding tabs quickly to reorder them.  Use an idle
  333.         # callback to check if none are selected so we do the Right Thing in
  334.         # this case.
  335.         eventloop.addUrgentCall(self.checkNoTabsSelected,
  336.                 "checkNoTabsSelected")
  337.  
  338.     def checkNoTabsSelected(self):
  339.         if len(self.currentSelection) == 0:
  340.             prevTab = self.currentView.cur()
  341.             if prevTab is None:
  342.                 # we remove the 1st tab in the list, try to select the new 1st
  343.                 # tab
  344.                 prevTab = self.currentView.getNext()
  345.             if prevTab is None:
  346.                 # That was the last tab in the list, select the guide
  347.                 self.selectFirstTab()
  348.             else:
  349.                 self.selectItem(self.currentView, prevTab.objID())
  350.             self.handler.displayCurrentTabContent()
  351.  
  352.     def selectFirstTab(self):
  353.         if config.get(prefs.OPEN_CHANNEL_ON_STARTUP) is not None:
  354.             view = views.feeds.filterWithIndex(indexes.feedsByURL,
  355.                           unicode(config.get(prefs.OPEN_CHANNEL_ON_STARTUP)))
  356.             if len(view) > 0:
  357.                 self.selectItem(views.allTabs, view[0].getID())
  358.                 self.handler.displayCurrentTabContent()
  359.                 view.unlink()
  360.                 return
  361.             else:
  362.                 view.unlink()
  363.         if config.get(prefs.OPEN_FOLDER_ON_STARTUP) is not None:
  364.             view = views.channelFolders.filterWithIndex(indexes.foldersByTitle,
  365.                           unicode(config.get(prefs.OPEN_FOLDER_ON_STARTUP)))
  366.             if len(view) > 0:
  367.                 self.selectItem(views.allTabs, view[0].getID())
  368.                 self.handler.displayCurrentTabContent()
  369.                 view.unlink()
  370.                 return
  371.             else:
  372.                 view.unlink()
  373.  
  374.         views.guideTabs.resetCursor()
  375.         guide = views.guideTabs.getNext()
  376.         self.selectItem(views.guideTabs, guide.objID())
  377.         self.handler.displayCurrentTabContent()
  378.  
  379.     def isFolderSelected(self):
  380.         """Returns if a channel/playlist folder is selected."""
  381.         for tab in self.getObjects():
  382.             if isinstance(tab.obj, folder.FolderBase):
  383.                 return True
  384.         return False
  385.  
  386. class SelectionHandler(object):
  387.     """Handles selection for Democracy.
  388.  
  389.     Attributes:
  390.  
  391.     tabListSelection -- SelectionArea for the tab list
  392.     itemListSelection -- SelectionArea for the item list
  393.     tabListActive -- does the tabListSelection the have the "active"
  394.         selection?  In other words, is that the one that was clicked on last.
  395.     """
  396.  
  397.     def __init__(self):
  398.         self.tabListSelection = TabSelectionArea(self)
  399.         self.itemListSelection = SelectionArea(self)
  400.         self.lastDisplay = None
  401.         self.tabListActive = True
  402.  
  403.     def getSelectionForArea(self, area):
  404.         if area == 'tablist':
  405.             return self.tabListSelection
  406.         elif area == 'itemlist':
  407.             return self.itemListSelection
  408.         else:
  409.             raise ValueError("Unknown area: %s" % area)
  410.  
  411.     def isSelected(self, area, view, id):
  412.         return self.getSelectionForArea(area).isSelected(view, id)
  413.  
  414.     def selectItem(self, area, view, id, shiftSelect, controlSelect, displayTabContent=True):
  415.         selection = self.getSelectionForArea(area)
  416.         try:
  417.             selectedObj = view.getObjectByID(id)
  418.         except database.ObjectNotFoundError:
  419.             # Item got deleted before the select went through.
  420.             return
  421.  
  422.         # ignore control and shift when selecting static tabs
  423.         if (isinstance(selectedObj, tabs.Tab) and 
  424.                 selectedObj.type in ('statictab', 'guide')):
  425.             controlSelect = shiftSelect = False
  426.  
  427.         if controlSelect:
  428.             selection.toggleItemSelect(view, id)
  429.         elif shiftSelect:
  430.             selection.extendSelection(view, id)
  431.         else:
  432.             selection.clearSelection()
  433.             selection.selectItem(view, id)
  434.  
  435.         if area == 'itemlist':
  436.             self.setTabListActive(False)
  437.             self.updateMenus()
  438.         else:
  439.             self.setTabListActive(True)
  440.             if displayTabContent:
  441.                 self.displayCurrentTabContent()
  442.  
  443.     def setTabListActive(self, value):
  444.         self.tabListActive = value
  445.         self.tabListSelection.setObjectsActive(value)
  446.         self.itemListSelection.setObjectsActive(not value)
  447.  
  448.     def calcSelection(self, area, sourceID):
  449.         """Calculate the selection, given the ID of an object that was clicked
  450.         on.  If sourceID is in the current selection, this will all the
  451.         objects in the current selection, otherwise it will be only the object
  452.         that corresponds to sourceID.  
  453.         """
  454.  
  455.         selection = self.getSelectionForArea(area)
  456.         if sourceID in selection.currentSelection:
  457.             return set(selection.currentSelection)
  458.         else:
  459.             return set([sourceID])
  460.  
  461.     def selectFirstTab(self):
  462.         self.tabListSelection.selectFirstTab()
  463.  
  464.     def selectTabByTemplateBase(self, tabTemplateBase, displayTabContent=True):
  465.         tabViews = [ 
  466.             views.guideTabs, 
  467.             views.staticTabs, 
  468.             views.feedTabs, 
  469.             views.playlistTabs,
  470.         ]
  471.         for view in tabViews:
  472.             for tab in view:
  473.                 if tab.tabTemplateBase == tabTemplateBase:
  474.                     self.selectItem('tablist', view, tab.objID(),
  475.                             shiftSelect=False, controlSelect=False,
  476.                             displayTabContent=displayTabContent)
  477.                     return
  478.  
  479.     def selectTabByObject(self, obj, displayTabContent=True):
  480.         channelTabOrder = util.getSingletonDDBObject(views.channelTabOrder)
  481.         playlistTabOrder = util.getSingletonDDBObject(views.playlistTabOrder)
  482.         tabViews = [ 
  483.             views.guideTabs, 
  484.             views.staticTabs, 
  485.             channelTabOrder.getView(), 
  486.             playlistTabOrder.getView(), 
  487.         ]
  488.         for view in tabViews:
  489.             for tab in view:
  490.                 if tab.obj is obj:
  491.                     self.selectItem('tablist', view, tab.objID(),
  492.                             shiftSelect=False, controlSelect=False,
  493.                             displayTabContent=displayTabContent)
  494.                     return
  495.  
  496.     def _chooseDisplayForCurrentTab(self):
  497.         tls = self.tabListSelection
  498.         frame = app.controller.frame
  499.  
  500.         if len(tls.currentSelection) == 0:
  501.             raise AssertionError("No tabs selected")
  502.         elif len(tls.currentSelection) == 1:
  503.             for id in tls.currentSelection:
  504.                 tab = tls.currentView.getObjectByID(id)
  505.                 return app.TemplateDisplay(tab.contentsTemplate,
  506.                                            tab.templateState,
  507.                         frameHint=frame, areaHint=frame.mainDisplay, 
  508.                         id=tab.obj.getID())
  509.         else:
  510.             foldersSelected = False
  511.             type = tls.getType()
  512.             if type == 'playlisttab':
  513.                 templateName = 'multi-playlist'
  514.             elif type == 'channeltab':
  515.                 templateName = 'multi-channel'
  516.             selectedChildren = 0
  517.             selectedFolders = 0
  518.             containedChildren = 0
  519.             for tab in self.getSelectedTabs():
  520.                 if isinstance(tab.obj, folder.FolderBase):
  521.                     selectedFolders += 1
  522.                     view = tab.obj.getChildrenView()
  523.                     containedChildren += view.len()
  524.                     for child in view:
  525.                         if child.getID() in tls.currentSelection:
  526.                             selectedChildren -= 1
  527.                 else:
  528.                     selectedChildren += 1
  529.             return app.TemplateDisplay(templateName,'default', frameHint=frame,
  530.                     areaHint=frame.mainDisplay,
  531.                     selectedFolders=selectedFolders,
  532.                     selectedChildren=selectedChildren,
  533.                     containedChildren=containedChildren)
  534.  
  535.     def updateMenus(self):
  536.         tabTypes = self.tabListSelection.getTypesDetailed()
  537.         if tabTypes.issubset(set(['guidetab', 'addedguidetab'])):
  538.             guideURL = self.getSelectedTabs()[0].obj.getURL()
  539.         else:
  540.             guideURL = None
  541.         multiple = len(self.tabListSelection.currentSelection) > 1
  542.  
  543.         actionGroups = {}
  544.         states = {"plural":[],
  545.                   "folders":[],
  546.                   "folder":[]}
  547.  
  548.         is_playlistlike = tabTypes.issubset (set(['playlisttab', 'playlistfoldertab']))
  549.         is_channellike = tabTypes.issubset (set(['channeltab', 'channelfoldertab', 'addedguidetab']))
  550.         is_channel = tabTypes.issubset (set(['channeltab', 'channelfoldertab']))
  551.         if len (tabTypes) == 1:
  552.             if multiple:
  553.                 if 'playlisttab' in tabTypes:
  554.                     states["plural"].append("RemovePlaylists")
  555.                 elif 'playlistfoldertab' in tabTypes:
  556.                     states["folders"].append("RemovePlaylists")
  557.                 elif 'channeltab' in tabTypes:
  558.                     states["plural"].append("RemoveChannels")
  559.                 elif 'channelfoldertab' in tabTypes:
  560.                     states["folders"].append("RemoveChannels")
  561.                 elif 'addedguidetab' in tabTypes:
  562.                     states["plural"].append("ChannelGuides")
  563.             else:
  564.                 if 'playlisttab' in tabTypes:
  565.                     pass
  566.                 elif 'playlistfoldertab' in tabTypes:
  567.                     states["folder"].append("RemovePlaylists")
  568.                 elif 'channeltab' in tabTypes:
  569.                     pass
  570.                 elif 'channelfoldertab' in tabTypes:
  571.                     states["folder"].append("RemoveChannels")
  572.                 elif 'addedguidetab' in tabTypes:
  573.                     pass
  574.  
  575.         if multiple and is_channel:
  576.             states["plural"].append("UpdateChannels")
  577.  
  578.         actionGroups["ChannelLikeSelected"] = is_channellike and not multiple
  579.         actionGroups["ChannelLikesSelected"] = is_channellike
  580.         actionGroups["PlaylistLikeSelected"] = is_playlistlike and not multiple
  581.         actionGroups["PlaylistLikesSelected"] = is_playlistlike
  582.         actionGroups["ChannelSelected"] = tabTypes.issubset (set(['channeltab'])) and not multiple
  583.         actionGroups["ChannelsSelected"] = tabTypes.issubset (set(['channeltab', 'channelfoldertab']))
  584.         actionGroups["ChannelFolderSelected"] = tabTypes.issubset(set(['channelfoldertab'])) and not multiple
  585.  
  586.         # Handle video item area.
  587.         actionGroups["VideoSelected"] = False
  588.         actionGroups["VideosSelected"] = False
  589.         actionGroups["VideoPlayable"] = False
  590.         videoFileName = None
  591.         if 'downloadeditem' in self.itemListSelection.getTypesDetailed():
  592.             actionGroups["VideosSelected"] = True
  593.             actionGroups["VideoPlayable"] = True
  594.             if len(self.itemListSelection.currentSelection) == 1:
  595.                 actionGroups["VideoSelected"] = True
  596.                 item = self.itemListSelection.getObjects()[0]
  597.                 videoFileName = item.getVideoFilename()
  598.             else:
  599.                 states["plural"].append("RemoveVideos")
  600. #        if len(self.itemListSelection.currentSelection) == 0:
  601. #            if playable_videos:
  602. #                actionGroups["VideoPlayable"] = True
  603.  
  604.         app.controller.frame.onSelectedTabChange(states, actionGroups, 
  605.                 guideURL, videoFileName)
  606.  
  607.     def displayCurrentTabContent(self):
  608.         frame = app.controller.frame
  609.         mainDisplay = frame.getDisplay(frame.mainDisplay)
  610.  
  611.         # Hack to avoid re-displaying channel template
  612.         if (mainDisplay and hasattr(mainDisplay, 'templateName') and mainDisplay.templateName == 'channel'):
  613.             tls = self.tabListSelection
  614.             if len(tls.currentSelection) == 1:
  615.                 for id in tls.currentSelection:
  616.                     tab = tls.currentView.getObjectByID(id)
  617.                     if tab.contentsTemplate == 'channel':
  618.                         newId = int(tab.obj.getID())
  619.                         #print "swapping templates %d %d" % (mainDisplay.kargs['id'], newId)
  620.                                                         
  621.                         self.itemListSelection.clearSelection()
  622.                         self.updateMenus()
  623.                         if mainDisplay.kargs['id'] != newId:
  624.                             mainDisplay.reInit(id = newId)
  625.                         return
  626.         newDisplay = self._chooseDisplayForCurrentTab()
  627.  
  628.         # Don't redisplay the current tab if it's being displayed.  It messes
  629.         # up our database callbacks.  The one exception is the guide tab,
  630.         # where redisplaying it will reopen the home page.
  631.         if (self.lastDisplay and newDisplay == self.lastDisplay and
  632.                 self.lastDisplay is mainDisplay and
  633.                 newDisplay.templateName != 'guide'):
  634.             newDisplay.unlink()
  635.             return
  636.  
  637.         self.itemListSelection.clearSelection()
  638.         self.updateMenus()
  639.         # do a queueSelectDisplay to make sure that the selectDisplay gets
  640.         # executed after our changes to the tablist template.  This makes tab
  641.         # selection feel faster because the selection changes quickly.
  642.         template.queueSelectDisplay(frame, newDisplay, frame.mainDisplay)
  643.         self.lastDisplay = newDisplay
  644.  
  645.     def isTabSelected(self, tab):
  646.         return tab.objID() in self.tabListSelection.currentSelection
  647.  
  648.     def getSelectedTabs(self):
  649.         """Return a list of the currently selected Tabs. """
  650.  
  651.         return self.tabListSelection.getObjects()
  652.  
  653.     def getSelectedItems(self):
  654.         """Return a list of the currently selected items. """
  655.  
  656.         return self.itemListSelection.getObjects()
  657.